home *** CD-ROM | disk | FTP | other *** search
- /* variables.c -- Functions for hacking shell variables. */
-
- /* Copyright (C) 1987,1989 Free Software Foundation, Inc.
-
- This file is part of GNU Bash, the Bourne Again SHell.
-
- Bash is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- Bash is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
- License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Bash; see the file COPYING. If not, write to the Free
- Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <pwd.h>
- #include <sys/types.h>
-
- #include "shell.h"
- #include "hash.h"
- #include "flags.h"
-
- #if defined (USG) && !defined (isc386) && !defined (sgi)
- struct passwd *getpwuid (), *getpwent ();
- #endif
-
- /* The list of shell variables that the user has created, or that came from
- the environment. */
- HASH_TABLE *shell_variables = (HASH_TABLE *)NULL;
-
- /* The list of shell functions that the user has created, or that came from
- the environment. */
- HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
-
- /* The current variable context. This is really a count of how deep into
- executing functions we are. */
- int variable_context = 0;
-
- /* The array of shell assignments which are made only in the environment
- for a single command. */
- char **temporary_env = (char **)NULL;
-
- /* Some funky variables which are known about specially. Here is where
- "$*", "$1", and all the cruft is kept. */
- char *dollar_vars[10];
- WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
-
- /* The value of $$. */
- int dollar_dollar_pid;
-
- /* An array which is passed to commands as their environment. It is
- manufactured from the overlap of the initial environment and the
- shell variables that are marked for export. */
- char **export_env = (char **)NULL;
-
- /* Non-zero means that we have to remake EXPORT_ENV. */
- int array_needs_making = 1;
-
- /* The list of variables that may not be unset in this shell. */
- char **non_unsettable_vars = (char **)NULL;
-
- #if defined (USG)
- #define DEFAULT_MAIL_PATH "/usr/mail/"
- #else
- #define DEFAULT_MAIL_PATH "/usr/spool/mail/"
- #endif
-
- /* Some forward declarations. */
- SHELL_VAR *set_if_not (); /* returns new or existing entry */
- static void sbrand (); /* set bash random number generator */
-
- /* Make VAR be auto-exported. VAR is a pointer to a SHELL_VAR. */
- #define set_auto_export(var) \
- { var->attributes |= att_exported; array_needs_making = 1; }
-
- /* Initialize the shell variables from the current environment. */
- initialize_shell_variables (env)
- char *env[];
- {
- extern char *primary_prompt, *secondary_prompt;
- char *name, *string;
- int c, char_index;
- int string_index = 0;
- SHELL_VAR *temp_var;
-
- if (!shell_variables)
- shell_variables = make_hash_table (0);
-
- if (!shell_functions)
- shell_functions = make_hash_table (0);
-
- /* Set up $PWD. */
- {
- char *get_working_directory (), *cd;
-
- cd = get_working_directory ("shell-init");
- if (cd)
- {
- bind_variable ("PWD", cd);
- free (cd);
- }
- }
-
- while (string = env[string_index++])
- {
- char_index = 0;
-
- name = (char *)alloca (1 + strlen (string));
-
- while ((c = *string++) && c != '=')
- name[char_index++] = c;
-
- name[char_index] = '\0';
-
- /* If exported function, define it now. */
- /* Posix.2 style exported function: name()=value */
- if (strncmp ("() {", string, 4) == 0 ||
- ((name[char_index - 1] == ')' &&
- name[char_index - 2] == '(' &&
- string[0] == '{')))
- {
- char *eval_string;
-
- eval_string = (char *)xmalloc (3 + strlen (string) + strlen (name));
- sprintf (eval_string, "%s %s", name, string);
- parse_and_execute (eval_string, name);
-
- if (name[char_index - 1] == ')')
- name[char_index - 2] = '\0';
- set_func_auto_export (name);
- }
- else
- {
- SHELL_VAR *v;
-
- v = bind_variable (name, string);
- set_auto_export (v);
- }
- }
-
- /* Remember this pid. */
- dollar_dollar_pid = (int)getpid ();
-
- /* Now make our own defaults in case the vars that we think are
- important are missing. */
- temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE);
- set_auto_export (temp_var);
-
- temp_var = set_if_not ("TERM", "dumb");
- set_auto_export (temp_var);
-
- set_if_not ("PS1", primary_prompt);
- set_if_not ("PS2", secondary_prompt);
- set_if_not ("IFS", " \t\n");
-
- /* Magic machine types. Pretty convenient. */
- temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
- set_auto_export (temp_var);
-
- /* Default MAILPATH, and MAILCHECK. */
- set_if_not ("MAILCHECK", "60");
- if ((get_string_value ("MAIL") == (char *)NULL) &&
- (get_string_value ("MAILPATH") == (char *)NULL))
- {
- extern char *current_user_name;
- char *tem;
-
- tem = (char *)xmalloc (1 + sizeof (DEFAULT_MAIL_PATH)
- + strlen (current_user_name));
- strcpy (tem, DEFAULT_MAIL_PATH);
- strcat (tem, current_user_name);
-
- bind_variable ("MAILPATH", tem);
- free (tem);
- }
-
- /* Do some things with shell level. */
- temp_var = set_if_not ("SHLVL", "0");
- set_auto_export (temp_var);
- adjust_shell_level (1);
-
- /* Make a variable $PPID, which holds the pid of the shell's parent. */
- {
- char *ppid;
- SHELL_VAR *v;
- extern char *itos ();
-
- ppid = itos ((int) getppid ());
- v = find_variable ("PPID");
-
- if (v)
- v->attributes &= ~att_readonly;
-
- v = bind_variable ("PPID", ppid);
- v->attributes |= (att_readonly | att_integer);
-
- non_unsettable ("PPID");
- free (ppid);
- }
-
- #if defined (GETOPTS_BUILTIN)
- /* Initialize the `getopts' stuff. */
- bind_variable ("OPTIND", "1");
- bind_variable ("OPTERR", "1");
- #endif /* GETOPTS_BUILTIN */
-
- /* Get the full pathname to THIS shell, and set the BASH variable
- to it. */
- {
- extern char *shell_name, *find_user_command (), *full_pathname ();
- extern int login_shell;
- char *tname = find_user_command (shell_name);
-
- if ((login_shell == 1) && (*shell_name != '/'))
- {
- struct passwd *entry = getpwuid (getuid ());
-
- if (entry)
- {
- /* If HOME doesn't exist, set it. */
- temp_var = (SHELL_VAR *)find_variable ("HOME");
- if (!temp_var)
- {
- temp_var = bind_variable ("HOME", entry->pw_dir);
- temp_var->attributes |= att_exported;
- }
- name = savestring (entry->pw_shell);
- }
- else
- name = savestring ("a.out");
- endpwent ();
- }
- else
- {
- if (!tname)
- {
- char *make_absolute ();
- name = make_absolute (shell_name, get_string_value ("PWD"));
- }
- else
- {
- name = full_pathname (tname);
- free (tname);
- }
- }
-
- /* Make the exported environment variable SHELL be whatever the name of
- this shell is. Note that the `tset' command looks at this variable
- to determine what style of commands to output; if it ends in "csh",
- then C-shell commands are output, else Bourne shell commands. */
- temp_var = set_if_not ("SHELL", name);
- set_auto_export (temp_var);
-
- /* Make a variable called BASH, which is the name of THIS shell. */
- temp_var = bind_variable ("BASH", name);
- temp_var->attributes |= att_exported;
-
- free (name);
- }
-
- /* Make a variable called BASH_VERSION which contains the version info. */
- {
- char tt[12];
- extern char *dist_version;
- extern int build_version;
-
- sprintf (tt, "%s.%d", dist_version, build_version);
- bind_variable ("BASH_VERSION", tt);
- }
-
- /* Set history variables to defaults, and then do whatever we would
- do if the variable had just been set. */
- {
- char *tilde_expand ();
- char *tem = tilde_expand ("~/.bash_history");
-
- set_if_not ("HISTFILE", tem);
- free (tem);
-
- set_if_not ("HISTSIZE", "500");
- sv_histsize ("HISTSIZE");
- }
-
- /* seed the random number generator */
-
- sbrand (dollar_dollar_pid);
-
- /* If we have inherited `noclobber' from a previous shell, then set
- noclobbering on. */
- {
- extern int noclobber;
-
- noclobber = find_variable ("noclobber") != NULL;
- }
-
- /* Initialize the dynamic variables, and seed their values */
- initialize_dynamic_variables ();
-
- non_unsettable ("PATH");
- non_unsettable ("PS1");
- non_unsettable ("PS2");
- non_unsettable ("IFS");
-
- /* Get the users real user id, and save that in an readonly variable.
- To make the variable *really* readonly, we have added it to a special
- list of vars. */
-
- sv_uids ();
- set_var_read_only ("UID");
- set_var_read_only ("EUID");
-
- non_unsettable ("EUID");
- non_unsettable ("UID");
- }
-
- adjust_shell_level (change)
- int change;
- {
- extern int shell_level;
- extern char *itos ();
- char *new_level;
- int old_level;
-
-
- old_level = atoi (get_string_value ("SHLVL"));
- shell_level = old_level + change;
- new_level = itos (shell_level);
- bind_variable ("SHLVL", new_level);
- free (new_level);
- }
-
- /* Add NAME to the list of variables that cannot be unset
- if it isn't already there. */
- non_unsettable (name)
- char *name;
- {
- register int i;
-
- if (!non_unsettable_vars)
- {
- non_unsettable_vars = (char **)xmalloc (1 * sizeof (char *));
- non_unsettable_vars[0] = (char *)NULL;
- }
-
- for (i = 0; non_unsettable_vars[i]; i++)
- if (strcmp (non_unsettable_vars[i], name) == 0)
- return;
-
- non_unsettable_vars = (char **)
- xrealloc (non_unsettable_vars, (2 + i) * sizeof (char *));
- non_unsettable_vars[i] = savestring (name);
- non_unsettable_vars[i + 1] = (char *)NULL;
- }
-
- /* Set NAME to VALUE if NAME has no value. */
- SHELL_VAR *
- set_if_not (name, value)
- char *name, *value;
- {
- SHELL_VAR *v = find_variable (name);
-
- if (!v)
- v = bind_variable (name, value);
- return (v);
- }
-
- /* Map FUNCTION over the variables in VARIABLES. Return an array of the
- variables that satisfy FUNCTION. Satisfy means that FUNCTION returns
- a non-zero value for. A NULL value for FUNCTION means to use all
- variables. */
- SHELL_VAR **
- map_over (function, var_hash_table)
- Function *function;
- HASH_TABLE* var_hash_table;
- {
- register int i;
- register BUCKET_CONTENTS *tlist;
- SHELL_VAR *var, **list = (SHELL_VAR **)NULL;
- int list_index = 0, list_size = 0;
-
- for (i = 0; i < var_hash_table->nbuckets; i++)
- {
- tlist = get_hash_bucket (i, var_hash_table);
-
- while (tlist)
- {
- var = (SHELL_VAR *)tlist->data;
-
- if (!function || (*function) (var))
- {
- if (list_index + 1 >= list_size)
- list = (SHELL_VAR **)
- xrealloc (list, (list_size += 20) * sizeof (SHELL_VAR *));
-
- list[list_index++] = var;
- list[list_index] = (SHELL_VAR *)NULL;
- }
- tlist = tlist->next;
- }
- }
- return (list);
- }
-
- int
- qsort_var_comp (var1, var2)
- SHELL_VAR **var1, **var2;
- {
- return (strcmp ((*var1)->name, (*var2)->name));
- }
-
- sort_variables (array)
- SHELL_VAR **array;
- {
- qsort (array, array_len (array), sizeof (SHELL_VAR *), qsort_var_comp);
- }
-
- /* Create a NULL terminated array of all the shell variables in TABLE. */
- static SHELL_VAR **
- all_vars (table)
- HASH_TABLE *table;
- {
- SHELL_VAR **list;
-
- list = map_over ((Function *)NULL, table);
- if (list)
- sort_variables (list);
- return (list);
- }
-
- /* Create a NULL terminated array of all the shell variables. */
- SHELL_VAR **
- all_shell_variables ()
- {
- return (all_vars (shell_variables));
- }
-
- /* Create a NULL terminated array of all the shell functions. */
- SHELL_VAR **
- all_shell_functions ()
- {
- return (all_vars (shell_functions));
- }
-
- /* Print VARS to stdout in such a way that they can be read back in. */
- print_var_list (list)
- register SHELL_VAR **list;
- {
- register int i;
- register SHELL_VAR *var;
-
- for (i = 0; list && (var = list[i]); i++)
- if (!invisible_p (var))
- print_assignment (var);
- }
-
- #if defined (NOTDEF)
- /* Print LIST (a linked list of shell variables) to stdout
- by printing the names, without the values. Used to support the
- `set +' command. */
- print_vars_no_values (list)
- register SHELL_VAR **list;
- {
- register int i;
- register SHELL_VAR *var;
-
- for (i = 0; list && (var = list[i]); i++)
- if (!invisible_p (var))
- printf ("%s\n", var->name);
- }
- #endif
-
- /* Print the value of a single SHELL_VAR. No newline is
- output, but the variable is printed in such a way that
- it can be read back in. */
- print_assignment (var)
- SHELL_VAR *var;
- {
- if (function_p (var) && var->value)
- {
- printf ("%s=", var->name);
- print_var_function (var);
- printf ("\n");
- }
- else if (var->value)
- {
- printf ("%s=", var->name);
- print_var_value (var);
- printf ("\n");
- }
- }
-
- /* Print the value cell of VAR, a shell variable. Do not print
- the name, nor leading/trailing newline. */
- print_var_value (var)
- SHELL_VAR *var;
- {
- if (var->value)
- printf ("%s", var->value);
- }
-
- /* Print the function cell of VAR, a shell variable. Do not
- print the name, nor leading/trailing newline. */
- print_var_function (var)
- SHELL_VAR *var;
- {
- char *named_function_string ();
-
- if (function_p (var) && var->value)
- printf ("%s", named_function_string ((char *)NULL, var->value, 1));
- }
-
-
- /* **************************************************************** */
- /* */
- /* Dynamic Variable Extension */
- /* */
- /* **************************************************************** */
-
- /* DYNAMIC VARIABLES
-
- These are variables whose values are generated anew each time they are
- referenced. These are implemented using a pair of function pointers
- in the struct variable: assign_func, which is called from bind_variable,
- and dynamic_value, which is called from find_variable.
-
- assign_func is called from bind_variable, if bind_variable discovers
- that the variable being assigned to has such a function. The function
- is called as
- SHELL_VAR *temp = (*(entry->assign_func)) (entry, value)
- and the (SHELL_VAR *)temp is returned as the value of bind_variable. It
- is usually ENTRY (self).
-
- dynamic_value is called from find_variable to return a `new' value for
- the specified dynamic varible. If this function is NULL, the variable
- is treated as a `normal' shell variable. If it is not, however, then
- this function is called like this:
- tempvar = (*(var->dynamic_value)) (var);
-
- Sometimes `tempvar' will replace the value of `var'. Other times, the
- shell will simply use the string value. Pretty object-oriented, huh?
-
- Be warned, though: if you `unset' a special variable, it loses its
- special meaning, even if you subsequently set it.
-
- The special assignment code would probably have been better put in
- subst.c: do_assignment, in the same style as
- stupidly_hack_special_variables, but I wanted the changes as
- localized as possible. */
-
- /* The value of $SECONDS. This is the number of seconds since shell
- invocation, or, the number of seconds since the last assignment + the
- value of the last assignment. */
- static long seconds_value_assigned = (long)0;
-
- /* Originally defined in shell.c */
- extern time_t shell_start_time;
-
- SHELL_VAR *
- assign_seconds (self, value)
- SHELL_VAR *self;
- char *value;
- {
- seconds_value_assigned = atol (value);
- shell_start_time = NOW;
- return (self);
- }
-
- SHELL_VAR *
- get_seconds (var)
- SHELL_VAR *var;
- {
- extern char *itos ();
- time_t time_since_start;
- char *p;
-
- time_since_start = NOW - shell_start_time;
- p = itos((int) seconds_value_assigned + time_since_start);
-
- if (var->value)
- free (var->value);
-
- var->attributes |= att_integer;
- var->value = p;
- return (var);
- }
-
- /* The random number seed. You can change this by setting RANDOM. */
- static unsigned long rseed = 1;
-
- /* A linear congruential random number generator based on the ANSI
- C standard. A more complicated one is overkill. */
-
- /* Returns a pseudo-random number between 0 and 32767. */
- static int
- brand ()
- {
- rseed = rseed * 1103515245 + 12345;
- return ((unsigned int)(rseed / 65536) % 32768);
- }
-
- /* Set the random number generator seed to SEED. */
- static void
- sbrand (seed)
- int seed;
- {
- rseed = seed;
- }
-
- static SHELL_VAR *
- assign_random (self, value)
- SHELL_VAR *self;
- char *value;
- {
- int s = atoi (value);
-
- sbrand (s);
- return (self);
- }
-
- static SHELL_VAR *
- get_random (var)
- SHELL_VAR *var;
- {
- int rv;
- char *p;
- extern char *itos ();
-
- rv = brand ();
- p = itos ((int)rv);
- if (var->value)
- free (var->value);
-
- var->attributes |= att_integer;
- var->value = p;
- return (var);
- }
-
- /* Function which returns the current line number. */
- static SHELL_VAR *
- get_lineno (var)
- SHELL_VAR *var;
- {
- extern int line_number;
- char *p;
- extern char *itos ();
-
- p = itos (line_number);
- if (var->value)
- free (var->value);
- var->value = p;
- return (var);
- }
-
- initialize_dynamic_variables ()
- {
- SHELL_VAR *v;
-
- v = bind_variable ("SECONDS", (char *)NULL);
- v->dynamic_value = get_seconds;
- v->assign_func = assign_seconds;
-
- v = bind_variable ("RANDOM", (char *)NULL);
- v->dynamic_value = get_random;
- v->assign_func = assign_random;
-
- v = bind_variable ("LINENO", (char *)NULL);
- v->dynamic_value = get_lineno;
- v->assign_func = (DYNAMIC_FUNC *)NULL;
- }
-
- /* How to get a pointer to the shell variable or function named NAME.
- HASHED_VARS is a pointer to the hash table containing the list
- of interest (either variables or functions). */
- SHELL_VAR *
- var_lookup (name, hashed_vars)
- char *name;
- HASH_TABLE *hashed_vars;
- {
- BUCKET_CONTENTS *bucket;
-
- bucket = find_hash_item (name, hashed_vars);
-
- if (bucket)
- return ((SHELL_VAR *)bucket->data);
- else
- return ((SHELL_VAR *)NULL);
- }
-
- /* Look up the variable entry whose name matches STRING.
- Returns the entry or NULL. */
- SHELL_VAR *
- find_variable (name)
- char *name;
- {
- extern int variable_context;
- extern Function *this_shell_builtin;
- SHELL_VAR *find_tempenv_variable ();
- SHELL_VAR *var = (SHELL_VAR *)NULL;
-
- /* If we are executing a function or builtin, first look in the
- temporary environment for the variable. This allows constructs
- such as "foo=x eval 'echo $foo'" to get the `exported' value
- of $foo. */
- if (variable_context || this_shell_builtin)
- var = find_tempenv_variable (name);
-
- if (!var)
- var = var_lookup (name, shell_variables);
-
- if (!var)
- return ((SHELL_VAR *)NULL);
-
- if (var->dynamic_value)
- return ((*(var->dynamic_value)) (var));
- else
- return (var);
- }
-
- /* Look up the function entry whose name matches STRING.
- Returns the entry or NULL. */
- SHELL_VAR *
- find_function (name)
- char *name;
- {
- return (var_lookup (name, shell_functions));
- }
-
- /* Return the string value of a variable. Return NULL if the variable
- doesn't exist, or only has a function as a value. Don't cons a new
- string. */
- char *
- get_string_value (var_name)
- char *var_name;
- {
- SHELL_VAR *var = find_variable (var_name);
-
- if (!var)
- return (char *)NULL;
- else
- return (var->value);
- }
-
- /* Create a local variable referenced by NAME. */
- SHELL_VAR *
- make_local_variable (name)
- char *name;
- {
- SHELL_VAR *new_var, *old_var, *bind_variable ();
- BUCKET_CONTENTS *elt;
-
- /* local foo; local foo; is a no-op. */
- old_var = find_variable (name);
- if (old_var && old_var->context == variable_context)
- return (old_var);
-
- elt = remove_hash_item (name, shell_variables);
- if (elt)
- {
- old_var = (SHELL_VAR *)elt->data;
- free (elt->key);
- free (elt);
- }
- else
- old_var = (SHELL_VAR *)NULL;
-
- /* If a variable does not already exist with this name, then
- just make a new one. */
- if (!old_var)
- {
- new_var = bind_variable (name, "");
- }
- else
- {
- new_var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-
- new_var->name = savestring (name);
- new_var->value = savestring ("");
-
- new_var->dynamic_value = (DYNAMIC_FUNC *)NULL;
- new_var->assign_func = (DYNAMIC_FUNC *)NULL;
-
- new_var->attributes = 0;
-
- if (exported_p (old_var))
- new_var->attributes |= att_exported;
-
- new_var->prev_context = old_var;
- elt = add_hash_item (savestring (name), shell_variables);
- elt->data = (char *)new_var;
- }
-
- new_var->context = variable_context;
- return (new_var);
- }
-
- /* Bind a variable NAME to VALUE. This conses up the name
- and value strings. */
- SHELL_VAR *
- bind_variable (name, value)
- char *name, *value;
- {
- SHELL_VAR *entry = var_lookup (name, shell_variables);
-
- if (!entry)
- {
- /* Make a new entry for this variable. Then do the binding. */
- entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-
- entry->attributes = 0;
- entry->name = savestring (name);
-
- if (value)
- entry->value = savestring (value);
- else
- entry->value = (char *)NULL;
-
- entry->dynamic_value = (DYNAMIC_FUNC *)NULL;
- entry->assign_func = (DYNAMIC_FUNC *)NULL;
-
- /* Always assume variables are to be made at toplevel!
- make_local_variable has the responsibilty of changing the
- variable context. */
- entry->context = 0;
- entry->prev_context = (SHELL_VAR *)NULL;
-
- {
- BUCKET_CONTENTS *elt;
-
- elt = add_hash_item (savestring (name), shell_variables);
- elt->data = (char *)entry;
- }
- }
- else if (entry->assign_func)
- return ((*(entry->assign_func)) (entry, value));
- else
- {
- if (readonly_p (entry))
- {
- report_error ("%s: read-only variable", name);
- return (entry);
- }
-
- /* If this variable has had its type set to integer (via `declare -i'),
- then do expression evaluation on it and store the result. The
- functions in expr.c (evalexp and bind_int_variable) are responsible
- for turning off the integer flag if they don't want further
- evaluation done. */
- if (integer_p (entry))
- {
- long val, evalexp();
- extern char *itos();
-
- val = evalexp (value);
- /* We cannot free () entry->value before this; what if the string
- we are working is `even=even+2'? We need the original value
- around while we are doing the evaluation to handle any possible
- recursion. */
- if (entry->value)
- free (entry->value);
-
- entry->value = itos (val);
- }
- else
- {
- if (entry->value)
- free (entry->value);
-
- if (value)
- entry->value = savestring (value);
- else
- entry->value = (char *)NULL;
- }
- }
-
- if (mark_modified_vars)
- entry->attributes |= att_exported;
-
- if (exported_p (entry))
- array_needs_making = 1;
-
- return (entry);
- }
-
- /* Dispose of the information attached to VAR. */
- dispose_variable (var)
- SHELL_VAR *var;
- {
- if (!var)
- return;
-
- if (function_p (var))
- dispose_command (var->value);
- else if (var->value)
- free (var->value);
-
- free (var->name);
-
- if (exported_p (var))
- array_needs_making = 1;
-
- free (var);
- }
-
- /* Unset the variable referenced by NAME. */
- unbind_variable (name)
- char *name;
- {
- SHELL_VAR *var = find_variable (name);
-
- if (!var)
- return (-1);
-
- if (var->value)
- {
- free (var->value);
- var->value = (char *)NULL;
- }
-
- makunbound (name, shell_variables);
-
- return (0);
- }
-
- /* Make the variable associated with NAME go away. HASH_LIST is the
- hash table from which this variable should be deleted (either
- shell_variables or shell_functions).
- Returns non-zero if the variable couldn't be found. */
- makunbound (name, hash_list)
- char *name;
- HASH_TABLE *hash_list;
- {
- BUCKET_CONTENTS *elt;
- SHELL_VAR *old_var, *new_var;
-
- elt = remove_hash_item (name, hash_list);
-
- if (!elt)
- return (-1);
-
- old_var = (SHELL_VAR *)elt->data;
- new_var = old_var->prev_context;
-
- if (old_var && exported_p (old_var))
- array_needs_making++;
-
- if (new_var)
- {
- /* Has to be a variable, functions don't have previous contexts. */
- BUCKET_CONTENTS *new_elt;
-
- new_elt = add_hash_item (savestring (new_var->name), hash_list);
- new_elt->data = (char *)new_var;
-
- if (exported_p (new_var))
- set_var_auto_export (new_var->name);
- }
-
- free (elt->key);
- free (elt);
-
- dispose_variable (old_var);
- stupidly_hack_special_variables (name);
- return (0);
- }
-
- /* Remove the variable with NAME if it is a local variable in the
- current context. */
- kill_local_variable (name)
- char *name;
- {
- SHELL_VAR *temp = find_variable (name);
-
- if (temp && (temp->context == variable_context))
- {
- makunbound (name, shell_variables);
- return (0);
- }
- return (-1);
- }
-
- /* Get rid of all of the variables in the current context. */
- int
- variable_in_context (var)
- SHELL_VAR *var;
- {
- return (var && var->context == variable_context);
- }
-
- kill_all_local_variables ()
- {
- register int i, pass;
- register SHELL_VAR *var, **list;
- HASH_TABLE *varlist;
-
- for (pass = 0; pass < 2; pass++)
- {
- varlist = pass ? shell_functions : shell_variables;
-
- list = map_over (variable_in_context, varlist);
-
- if (list)
- {
- for (i = 0; var = list[i]; i++)
- makunbound (var->name, varlist);
-
- free (list);
- }
- }
- }
-
- /* Delete the entire contents of the hash table. */
- delete_all_variables (hashed_vars)
- HASH_TABLE *hashed_vars;
- {
- register int i;
- register BUCKET_CONTENTS *bucket;
-
- for (i = 0; i < hashed_vars->nbuckets; i++)
- {
- bucket = hashed_vars->bucket_array[i];
-
- while (bucket)
- {
- BUCKET_CONTENTS *temp = bucket;
- SHELL_VAR *var, *prev;
-
- bucket = bucket->next;
-
- var = (SHELL_VAR *)temp->data;
-
- while (var)
- {
- prev = var->prev_context;
- dispose_variable (var);
-
- var = prev;
- }
-
- free (temp->key);
- free (temp);
- }
- hashed_vars->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
- }
- }
-
- SHELL_VAR *
- new_shell_variable (name)
- char *name;
- {
- SHELL_VAR *var;
-
- var = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-
- bzero (var, sizeof (SHELL_VAR));
- var->name = savestring (name);
- return (var);
- }
-
- /* Do a function binding to a variable. You pass the name and
- the command to bind to. This conses the name and command. */
- SHELL_VAR *
- bind_function (name, value)
- char *name;
- COMMAND *value;
- {
- SHELL_VAR *entry = find_function (name);
-
- if (!entry)
- {
- BUCKET_CONTENTS *elt;
-
- elt = add_hash_item (savestring (name), shell_functions);
-
- elt->data = (char *)new_shell_variable (name);
- entry = (SHELL_VAR *)elt->data;
- entry->dynamic_value = (DYNAMIC_FUNC *)NULL;
- entry->assign_func = (DYNAMIC_FUNC *)NULL;
-
- /* Functions are always made at the top level. This allows a
- function to define another function (like autoload). */
- entry->context = 0;
- }
-
- if (entry->value)
- dispose_command (entry->value);
-
- if (value) /* I don't think this can happen anymore */
- entry->value = (char *)copy_command (value);
- else
- entry->value = (char *)NULL;
-
- entry->attributes |= att_function;
-
- if (mark_modified_vars)
- entry->attributes |= att_exported;
-
- entry->attributes &= ~att_invisible; /* Just to be sure */
-
- array_needs_making = 1;
-
- return (entry);
- }
-
- /* Copy VAR to a new data structure and return that structure. */
- SHELL_VAR *
- copy_variable (var)
- SHELL_VAR *var;
- {
- SHELL_VAR *copy = (SHELL_VAR *)NULL;
-
- if (var)
- {
- copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-
- copy->attributes = var->attributes;
- copy->name = savestring (var->name);
-
- if (function_p (var))
- copy->value = (char *)copy_command (var->value);
- else if (var->value)
- copy->value = savestring (var->value);
- else
- copy->value = (char *)NULL;
-
- copy->dynamic_value = var->dynamic_value;
- copy->assign_func = var->assign_func;
-
- copy->context = var->context;
-
- /* Don't bother copying previous contexts along with this variable. */
- copy->prev_context = (SHELL_VAR *)NULL;
- }
- return (copy);
- }
-
- /* Make the variable associated with NAME be read-only.
- If NAME does not exist yet, create it. */
- set_var_read_only (name)
- char *name;
- {
- SHELL_VAR *entry = find_variable (name);
-
- if (!entry)
- {
- entry = bind_variable (name, "");
- if (!no_invisible_vars)
- entry->attributes |= att_invisible;
- }
- entry->attributes |= att_readonly;
- }
-
- /* Make the function associated with NAME be read-only.
- If NAME does not exist, we just punt, like auto_export code below. */
- set_func_read_only (name)
- char *name;
- {
- SHELL_VAR *entry = find_function (name);
-
- if (entry)
- entry->attributes |= att_readonly;
- }
-
- /* Make the variable associated with NAME be auto-exported.
- If NAME does not exist yet, create it. */
- set_var_auto_export (name)
- char *name;
- {
- SHELL_VAR *entry = find_variable (name);
-
- if (!entry)
- {
- entry = bind_variable (name, "");
- if (!no_invisible_vars)
- entry->attributes |= att_invisible;
- }
-
- set_auto_export (entry);
- }
-
- /* Make the function associated with NAME be auto-exported. */
- set_func_auto_export (name)
- char *name;
- {
- SHELL_VAR *entry = find_function (name);
-
- if (entry)
- {
- entry->attributes |= att_exported;
- array_needs_making = 1;
- }
- }
-
- /* Returns non-zero if STRING is an assignment statement. The returned value
- is the index of the `=' sign. */
- assignment (string)
- char *string;
- {
- register int c, index = 0;
-
- c = string[index];
-
- if (!isletter (c) && c != '_')
- return (0);
-
- while (c = string[index])
- {
- /* The following is safe. Note that '=' at the start of a word
- is not an assignment statement. */
- if (c == '=')
- return (index);
-
- if (!isletter (c) && !digit (c) && c != '_')
- return (0);
-
- index++;
- }
- return (0);
- }
-
- int
- visible_var (var)
- SHELL_VAR *var;
- {
- return (!invisible_p (var));
- }
-
- SHELL_VAR **
- all_visible_variables ()
- {
- SHELL_VAR **list;
-
- list = map_over (visible_var, shell_variables);
-
- if (list)
- sort_variables (list);
-
- return (list);
- }
-
- SHELL_VAR **
- all_visible_functions ()
- {
- SHELL_VAR **list;
-
- list = map_over (visible_var, shell_functions);
-
- if (list)
- sort_variables (list);
-
- return (list);
- }
-
- /* Return non-zero if the variable VAR is visible and exported. */
- int
- visible_and_exported (var)
- SHELL_VAR *var;
- {
- return (!invisible_p (var) && exported_p (var));
- }
-
- /* Make an array of assignment statements from the hash table
- HASHED_VARS which contains SHELL_VARs. Only visible, exported
- variables are eligible. */
- char **
- make_var_array (hashed_vars)
- HASH_TABLE *hashed_vars;
- {
- register int i, list_index;
- register SHELL_VAR *var;
- char **list = (char **)NULL;
- SHELL_VAR **vars;
-
- vars = map_over (visible_and_exported, hashed_vars);
-
- if (!vars)
- return (char **)NULL;
-
- list = (char **)xmalloc ((1 + array_len ((char **)vars)) * sizeof (char *));
-
- for (i = 0, list_index = 0; var = vars[i]; i++)
- {
- char *value, *named_function_string ();
-
- if (function_p (var))
- {
- value =
- named_function_string ((char *)NULL,
- (COMMAND *)function_cell (var), 0);
- }
- else
- value = value_cell (var);
-
- if (value)
- {
- #if 0
- list[list_index] =
- (char *)xmalloc (2 + strlen (var->name) + strlen (value));
-
- sprintf (list[list_index], "%s=%s", var->name, value);
- #else
- /* Let's see if this makes any kind of performance difference. */
- int name_len = strlen (var->name);
- int value_len = strlen (value);
- char *p;
-
- p = list[list_index] = (char *)xmalloc (2 + name_len + value_len);
- strcpy (p, var->name);
- p[name_len] = '=';
- strcpy (&p[name_len + 1], value);
- #endif
- list_index++;
- }
- }
-
- free (vars);
- list[list_index] = (char *)NULL;
- return (list);
- }
-
- /* Add STRING to the array of foo=bar strings that we already
- have to add to the environment. */
- assign_in_env (string)
- char *string;
- {
- int size;
-
- int offset = assignment (string);
- char *name = savestring (string);
- char *temp, *value = (char *)NULL;
-
- if (name[offset] == '=')
- {
- char *tilde_expand (), *string_list ();
- WORD_LIST *list, *expand_string_unsplit ();
-
- name[offset] = 0;
- temp = name + offset + 1;
- temp = tilde_expand (temp);
-
- list = expand_string_unsplit (temp, 0);
- value = string_list (list);
-
- if (list)
- dispose_words (list);
-
- free (temp);
- }
-
- if (!value) value = savestring ("");
-
- temp = (char *)xmalloc (2 + strlen (name) + strlen (value));
- sprintf (temp, "%s=%s", name, value);
- free (name);
-
- if (!temporary_env)
- {
- temporary_env = (char **)xmalloc (sizeof (char *));
- temporary_env [0] = (char *)NULL;
- }
-
- size = array_len (temporary_env);
- temporary_env = (char **)
- xrealloc (temporary_env, (size + 2) * (sizeof (char *)));
-
- temporary_env[size] = (temp);
- temporary_env[size + 1] = (char *)NULL;
- array_needs_making = 1;
-
- if (echo_command_at_execute)
- {
- /* The K*rn shell prints the `+ ' in front of assignment statements,
- so we do too. */
- extern char *indirection_level_string ();
- fprintf (stderr, "%s%s\n", indirection_level_string (), temp);
- fflush (stderr);
- }
- }
-
- /* Find a variable in the temporary environment that is named NAME.
- Return a consed variable, or NULL if not found. */
- SHELL_VAR *
- find_tempenv_variable (name)
- char *name;
- {
- register int i, l = strlen (name);
-
- if (!temporary_env)
- return ((SHELL_VAR *)NULL);
-
- for (i = 0; temporary_env[i]; i++)
- {
- if (strncmp (temporary_env[i], name, l) == 0 &&
- temporary_env[i][l] == '=')
- {
- SHELL_VAR *temp;
-
- temp = new_shell_variable (name);
-
- if (temporary_env[i][l + 1])
- temp->value = savestring (&temporary_env[i][l + 1]);
- else
- temp->value = savestring ("");
- temp->attributes = att_exported;
- temp->context = 0;
- temp->prev_context = (SHELL_VAR *)NULL;
-
- temp->dynamic_value = (DYNAMIC_FUNC *)NULL;
- temp->assign_func = (DYNAMIC_FUNC *)NULL;
-
- return (temp);
- }
- }
- return ((SHELL_VAR *)NULL);
- }
-
- /* Free the storage used in the variable array for temporary
- environment variables. */
- dispose_used_env_vars ()
- {
- if (!temporary_env)
- return;
-
- free_array (temporary_env);
- temporary_env = (char **)NULL;
- array_needs_making = 1;
- }
-
- /* Stupid comparison routine for qsort () ing strings. */
- int
- qsort_string_compare (s1, s2)
- register char **s1, **s2;
- {
- return (strcmp (*s1, *s2));
- }
-
- /* Sort ARRAY, a null terminated array of pointers to strings. */
- sort_char_array (array)
- char **array;
- {
- qsort (array, array_len (array), sizeof (char *), qsort_string_compare);
- }
-
- #define ISFUNC(s, o) ((s[o + 1] == '(') && (s[o + 2] == ')'))
-
- /* Add ASSIGN to ARRAY, or supercede a previous assignment in the
- array with the same left-hand side. Return the new array. */
- char **
- add_or_supercede (assign, array)
- char *assign;
- register char **array;
- {
- register int i;
- int equal_offset = assignment (assign);
-
- if (!equal_offset)
- return (array);
-
- /* If this is a function, then only supercede the function definition.
- We do this by including the `=(' in the comparison. */
- if (assign[equal_offset + 1] == '(')
- equal_offset++;
-
- for (i = 0; array[i]; i++)
- {
- if (STREQN (assign, array[i], equal_offset + 1))
- {
- free (array[i]);
- array[i] = savestring (assign);
- return (array);
- }
- }
- array = (char **)xrealloc (array, ((2 + i) * sizeof (char *)));
- array[i++] = savestring (assign);
- array[i] = (char *)NULL;
- return (array);
- }
-
- /* Make the environment array for the command about to be executed. If the
- array needs making. Otherwise, do nothing. If a shell action could
- change the array that commands receive for their environment, then the
- code should `array_needs_making++'. */
- maybe_make_export_env ()
- {
- register int i;
- register char **temp_array;
-
- if (array_needs_making)
- {
- if (export_env)
- free_array (export_env);
-
- #ifdef SHADOWED_ENV
- export_env =
- (char **)xmalloc ((1 + array_len (shell_environment)) * sizeof (char *));
-
- for (i = 0; shell_environment[i]; i++)
- export_env[i] = savestring (shell_environment[i]);
- export_env[i] = (char *)NULL;
-
- #else /* !SHADOWED_ENV */
-
- export_env = (char **)xmalloc (sizeof (char *));
- export_env[0] = (char *)NULL;
-
- #endif /* SHADOWED_ENV */
-
- temp_array = make_var_array (shell_variables);
- for (i = 0; temp_array && temp_array[i]; i++)
- export_env = add_or_supercede (temp_array[i], export_env);
- free_array (temp_array);
-
- temp_array = make_var_array (shell_functions);
- for (i = 0; temp_array && temp_array[i]; i++)
- export_env = add_or_supercede (temp_array[i], export_env);
- free_array (temp_array);
-
- if (temporary_env)
- {
- for (i = 0; temporary_env[i]; i++)
- export_env = add_or_supercede (temporary_env[i], export_env);
-
- /* Sort the array alphabetically. */
- sort_char_array (export_env);
- }
- array_needs_making = 0;
- }
- }
-
- /* We always put _ in the environment as the name of this command. */
- put_command_name_into_env (command_name)
- char *command_name;
- {
- char *dummy;
-
- dummy = (char *)xmalloc (4 + strlen (command_name));
-
- /* These three statements replace a call to sprintf */
- dummy[0] = '_';
- dummy[1] = '=';
- strcpy (&dummy[2], command_name);
- export_env = add_or_supercede (dummy, export_env);
- free (dummy);
- }
-
- /* We supply our own version of getenv () because we want library
- routines to get the changed values of exported variables. */
- char *last_tempenv_value = (char *)NULL;
-
- /* The NeXT C library has getenv () defined and used in the same file.
- This screws our scheme. However, Bash will run on the NeXT using
- the C library getenv (), since right now the only environment variable
- that we care about is HOME, and that is already defined. */
- #if defined (__STDC__)
- # define _CONST_HACK const
- #else
- # define _CONST_HACK
- #endif /* !__STDC__ */
-
- #if !defined (NeXT)
- char *
- getenv (name)
- char _CONST_HACK *name;
- {
- SHELL_VAR *var = find_tempenv_variable (name);
-
- if (var)
- {
- if (last_tempenv_value)
- free (last_tempenv_value);
-
- last_tempenv_value = savestring (value_cell (var));
- dispose_variable (var);
- return (last_tempenv_value);
- }
- else if (shell_variables)
- {
- var = find_variable (name);
- if (var && exported_p (var))
- return (value_cell (var));
- }
- else
- {
- register int i, len = strlen (name);
- extern char **environ;
-
- /* In some cases, s5r3 invokes getenv() before main(); BSD systems
- using gprof also exhibit this behavior. This means that
- shell_variables will be 0 when this is invoked. We look up the
- variable in the real environment in that case. */
-
- for (i = 0; environ[i]; i++)
- {
- if ((strncmp (environ[i], name, len) == 0) &&
- (environ[i][len] == '='))
- return (environ[i] + len + 1);
- }
- }
-
- return ((char *)NULL);
- }
- #endif /* NeXT */
-